// Shader to compute four-dimensional cellular automata
// 2005 Eric Rollins
// derived from helloGPU_GLSL.cpp from www.gpgpu.org

// texture global variable, set by application
uniform sampler2D texUnit;

// convert 2-d to 4-d coordinates
vec4 c2to4(vec2 vin){
   float rc = vin.x;
   float lw = vin.y;
   float bigrc = rc * 256.0;
   float r = floor(bigrc/16.0);
   float c = floor(mod(bigrc, 16.0));
   float biglw = lw * 256.0;
   float l = floor(biglw/16.0);
   float w = floor(mod(biglw, 16.0));
   return vec4(r,c,l,w);
}

// convert 4-d to 2-d coordinates
vec2 c4to2(vec4 vin){
   float rc = vin.x * 16.0 + vin.y;
   float lw = vin.z * 16.0 + vin.w;
   return vec2(rc/256.0, lw/256.0);
}

void main(void)
{
   //  read texture coordinates
   vec2 texCoord = gl_TexCoord[0].xy;
   //  read value of texture of current pixel
   vec4 baseValue  = texture2D(texUnit, texCoord);
   vec4 coord4 = c2to4(texCoord);
   //  don't fill in cells on edges (to avoid wrapping)
   float edgeMult = baseValue.w;
   //  find value of each neighbor
   vec4 pos1 = coord4 + vec4(+1.0, 0.0, 0.0, 0.0);
   vec4 val1 = texture2D(texUnit, c4to2(pos1));
   vec4 pos2 = coord4 + vec4(-1.0, 0.0, 0.0, 0.0);
   vec4 val2 = texture2D(texUnit, c4to2(pos2));
   vec4 pos3 = coord4 + vec4(0.0, +1.0, 0.0, 0.0);
   vec4 val3 = texture2D(texUnit, c4to2(pos3));
   vec4 pos4 = coord4 + vec4(0.0, -1.0, 0.0, 0.0);
   vec4 val4 = texture2D(texUnit, c4to2(pos4));
   vec4 pos5 = coord4 + vec4(0.0, 0.0, +1.0, 0.0);
   vec4 val5 = texture2D(texUnit, c4to2(pos5));
   vec4 pos6 = coord4 + vec4(0.0, 0.0, -1.0, 0.0);
   vec4 val6 = texture2D(texUnit, c4to2(pos6));
   vec4 pos7 = coord4 + vec4(0.0, 0.0, 0.0, +1.0);
   vec4 val7 = texture2D(texUnit, c4to2(pos7));
   vec4 pos8 = coord4 + vec4(0.0, 0.0, 0.0, -1.0);
   vec4 val8 = texture2D(texUnit, c4to2(pos8));
   //  our value is sum of all neighbors
   vec4 t1 = /*baseValue +*/ val1 + val2 + val3 + val4 + 
      val5 + val6 + val7 + val8;
   vec4 t2 = t1*edgeMult;
   gl_FragColor = t2;
}